﻿/* Derived from CART.DLL, Cart Plugin for KSP.
Original Author: Tony Chernogovsky, SPb, Russia.
Previous Maintainer: Callum King-Underwood, England.
Current Maintainer: Andrea Cappelletti, Milan, Italy aka CleverWalrus.
License: CC-BY-SA, Attribution & Share-alike Creative Common Licence. 
Feel free to modify as long as:
a) original author is mentioned, and 
b) you distribute your code under the same license.*/

using UnityEngine;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace CleverBobCat.LegacyCart
{
    public class CartVesselState
    {
        public bool controllable;
        public float mass;
        public bool onRemoteControl;
        public bool hasRemoteControlRoute;
        public double remoteControlDelay;
        public string menuHint;

        public void Update(Part part)
        {
            mass = 0;
            onRemoteControl = false;
            hasRemoteControlRoute = false;
            remoteControlDelay = 0;
            controllable = false;

            if (!part)
                return;

            bool hasMechJeb = false,
                 hasCommandPod = false,
                 needsCrew = false,
                 hasCrew = false;

            // Look for CommandPods, MechJebs, and RemoteTech.
            // I use Reflection here 'cause I don't want to know does a
            // player have those plugins installed (and I don't want to 
            // link with that plugin libraries either).

            if (part.vessel)
                foreach (Part p in part.vessel.parts)
                {
                    bool mechJeb = false,
                         remoteTech = false,
                         remotePod = false;

                    Type t = p.GetType(), t1 = t;

                    // Mono doesn't know how to compare System.Type with null. 
                    // LOL what a pussy :)))
                    while ((System.Object)t1 != null)
                    {
                        if (t1.Name == "Part") // stop searching
                            break;

                        if (t1.Name.StartsWith("MuMechJeb"))
                            mechJeb = true;
                        else if (t1.Name == "RemoteTech")
                            remoteTech = true;
                        else if (t1.Name == "RemotePod")
                            remotePod = true;
                        t1 = t1.BaseType;
                    }

                    if (mechJeb)
                        hasMechJeb = true;
                    if (p.protoModuleCrew.Count > 0)
                        hasCrew = true;
                    if (p.CrewCapacity > 0)
                        needsCrew = true;
                    if (p is CommandPod)
                        hasCommandPod = true;

                    if (remoteTech || remotePod)
                    {
                        // Once again, I use Reflection to get properties' values.
                        // I do not have a class prototype at this point.

                        BindingFlags b = BindingFlags.Public | BindingFlags.Instance;
                        PropertyInfo inRadioContact = t.GetProperty("inRadioContact", b),
                                     localControl = t.GetProperty("localControl", b),
                                     controlDelay = t.GetProperty("controlDelay", b);
                        try
                        {
                            if (((System.Object)inRadioContact != null) &&
                                ((System.Object)localControl != null) &&
                                ((System.Object)controlDelay != null))
                            {

                                onRemoteControl = !(bool)localControl.GetValue(p, null);
                                hasRemoteControlRoute = (bool)inRadioContact.GetValue(p, null);
                                remoteControlDelay = (double)controlDelay.GetValue(p, null);
                            }
                        }
                        catch { }
                    }

                    // Vessel mass...
                    mass += p.mass;
                }

            if (hasMechJeb)
                // disable RemoteTech if the vessel has MechJeb. 
                onRemoteControl = false;

            if (onRemoteControl)
                // crew don't count there. Just disable controls if the vessel 
                // needs crew, but does not have one (KSP issue: it blocks control 
                // axes in such a case).
                controllable = hasRemoteControlRoute && (hasCrew || !needsCrew);
            else
                // vessel's under local control, if there is
                // - either crew on-board, 
                // - or no need in crew, and 
                //   - either MechJeb, 
                //   - or unmanned command pod.
                controllable = hasCrew || ((hasMechJeb || hasCommandPod) && !needsCrew);

            controllable = controllable && part.vessel && part.vessel.isActiveVessel;

            if (controllable)
            {
                if (onRemoteControl)
                    menuHint = String.Format("RC delay {0:0.0}s", remoteControlDelay);
                else
                    menuHint = "";
            }
            else
            {
                if (!part.vessel)
                    menuHint = "No vessel";
                else if (!part.vessel.isActiveVessel)
                    menuHint = "It's not an active vessel";
                else if (needsCrew && !hasCrew)
                    menuHint = "No crew";
                else if (onRemoteControl && !hasRemoteControlRoute)
                    menuHint = "Out of RC";
                else
                    menuHint = "No control";
            }
        }
    }

    public struct CartControlState
    {
        public bool toggleLights;
        public bool toggleHandbrake;
        public bool toggleWheels;
        public bool toggleCruise;
        public bool brake;
        public double time;
    }

    public class CartControlList : List<CartControlState>
    {
        public CartControlList()
        {
        }

        public CartControlList(CartControlState s)
        {
            Add(s);
        }
    }

    public class CartControlQueue : Queue<CartControlState>
    {

        public CartControlList Delay(CartControlList state, double delay)
        {
            double t = 0;
            foreach (CartControlState s in state)
            {
                Enqueue(s);
                if (s.time > t)
                    t = s.time;
            }

            CartControlList ret = new CartControlList();
            CartControlState v;

            // We may need to dequeue _several_ records when delay decreases. 
            // Or no records at all, if it increases.
            while ((v = Peek()).time < t - delay)
            {
                ret.Add(v);
                Dequeue();
            }
            return ret;
        }
    }
}